<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/24
 * Time: 15:13
 * 基于角色的数据库方式验证类
 */

use think\Db;
use think\Config;
use think\Session;
use think\Request;
use think\Cache;
use think\cache\driver\Redis;
use app\admin\model\Menus;
use app\admin\model\RoleOperators;
use app\admin\model\RoleMenus;

class Rbac
{
    /**
     * 认证方法
     * @param $map
     * @param string $model
     * @return array|false|PDOStatement|string|\think\Model
     */
    public static function authenticate($map, $model = '')
    {
        if (!$model) {
            $model = Config::get('rbac.user_auth_model');
        }

        //使用给定的Map进行认证
        return Db::name($model)->where($map)->find();
    }

    /**
     * @todo  检查权限
     * @abstract 用户必须登录，否则跳转到登录页
     * @param type $action
     * @return boolean
     * @throws Exception
     */
    public static function checkAuth($action, $flag = false) {
        try {
            //必须登录
            if (!Session::has(Config::get('rbac.user_auth_key'))) {
                throw new Exception("location");
            }
            //如果是超级管理员，直接返回true
            $is_admin = self::isAdmin();
            if ($is_admin) {
                return true;
            }
            //必须传操作路径
            if (empty($action)) {
                throw new Exception("没有传操作参数：action！");
            }
            //必须存在用户名
            if (empty(Session::get('user_name'))) {
                throw new Exception("location");
            }
            //如果没有把权限写到session，则从DB里获取
            $menus = self::getRbacMenu();
            if (empty($menus)) {
                //检查该用户是否存在
                $menus = self::getAuthMenu(Session::get(Config::get('rbac.user_auth_key')));
                //储存权限
                self::setRbacMenu($menus);
            }
            //默认公有权限
            $default_actions = Config::get('rbac.default_actions');
            if (in_array($action, $default_actions)) {
                return true;
            }
            //$menus = json_decode(json_encode($menus), true);
            $model = new Menus();
            $menu_id = $model->getIdByMenuUrl($action);
            if(empty($menu_id)){
                return false;
            }
            //检查是有该菜单权限
            if (array_key_exists($menu_id, $menus)) {
                return ($flag) ? $menus[$menu_id] : true;
            } else {
                throw new Exception("没有权限！");
            }
        } catch (Exception $ex) {
            if ($ex->getMessage() == 'location') {
                //跳转到登录页
                header("location: /admin/Pub/login");
            } else {
                return false;
            }
        }
    }

    /**
     * @todo 获取一级菜单
     * @return boolean
     */
    public static function getTopMenu() {
        $menu = self::getRbacMenu();
        if (empty($menu)) {
            return false;
        }
        $list = array();
        foreach ($menu as $item) {
            if ($item['type'] == 'main' && $item['pid'] == 0) {
                $list[$item['id']] = $item;
            }
        }
        return $list;
    }

    /**
     * @todo 根据一级菜单组ID获取二级菜单
     * @param type $menu_id
     */
    public static function getGroupMenu($menu_id) {
        if (empty($menu_id)) {
            return false;
        }
        $menu = self::getRbacMenu();
        if (empty($menu)) {
            return false;
        }
        $list = array();
        //只有类型为main才可以显示在左菜单
        foreach ($menu as $item) {
            $pids = explode(',', $item['pids']);
            if (!in_array($menu_id, $pids) || $item['type'] != 'main') {
                continue;
            }
            $list[$item['pid']][] = $item;
            unset($pids);
        }
        return $list;
    }

    /**
     * @todo 返回储存权限索引
     * @return type
     */
    protected static function getRbacName() {
        $auth_id = Session::get(Config::get('rbac.user_auth_key'));
        return empty($auth_id) ? false : $auth_id . Config::get('rbac.redis_suffix');
    }

    /**
     * @todo 返回储存在session的菜单权限
     * @return boolean
     */
    private static function getRbacMenu() {
        $name = self::getRbacName();
        if (!$name) {
            return false;
        }
        $redis = new Redis();
        return $redis->get($name);
    }

    /**
     * @todo 储存菜单权限
     * @param type $menus
     */
    private static function setRbacMenu($menus) {
        $name = self::getRbacName();
        $redis = new Redis();
        $redis->set($name, $menus);
    }

    /**
     * @todo 根据用户ID获取权限菜单
     * @param type $operator_id
     * @return boolean|array
     */
    public static function getAuthMenu($operator_id) {
        if (empty($operator_id)) {
            return false;
        }
        //获取用户角色
        $model = new RoleOperators();
        $role_ids = $model->getRolesByOperatorId($operator_id);
        if (empty($role_ids)) {
            return false;
        }
        //根据角色获取菜单
        $model = new Menus();
        $menus = $model->getMenusByRoles($role_ids);
        return $menus;
    }


    /**
     * @todo判断是否超级管理员
     */
    public static function isAdmin() {
        if (!Session::has(Config::get('rbac.admin_auth_key'))) {
            return false;
        }
        //判断是否有储存了权限
        if (!self::getRbacMenu()) {
            //由DB获取储存
            $model = new Menus();
            $menus = $model->getAdminMenus();
            //储存权限
            self::setRbacMenu($menus);
        }
        return true;
    }

    /**
     * @todo 页面判断某个操作是否有权限
     * @param type $menu_url
     */
    public static function checkPageAuth($menu_url){
        //超级管理员直接返回TRUE
        if(self::isAdmin()){
            return true;
        }
        //获取菜单的id
        $model = new Menus();
        $menu_id = $model->getIdByMenuUrl($menu_url);
        if(empty($menu_id)){
            return false;
        }
        //其它用户检查权限
        $menu = self::getRbacMenu();
        if(!empty($menu) && array_key_exists($menu_id,$menu)){
            return true;
        }else{
            return false;
        }
    }
}